﻿using CodeGenerator.CSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Modifier = CodeGenerator.CSharp.Modifiers;

namespace CodeAngel.Domain
{
    /// <summary>
    /// Generates the entity files for an EntityDef:
    ///     * [entity name].cs - for end developers to add custom behavior, business 
    ///                          rules, etc. This file will only be created if it does 
    ///                          not already exist.
    ///     * [entity name].designer.cs - for auto-generated code
    /// </summary>
    public static class EntityFilesGenerator
    {
        #region CreateEntityFilesAsync

        /// <summary>
        /// Creates and writes to disk the entity files for the specified EntityDef.
        /// If the designer file already exists, it will be overwritten.  If the develoer
        /// file already exists, it will NOT be overwritten.
        /// </summary>
        public static Task CreateEntityFilesAsync(this EntityDef thisED)
        {
            return Task.Factory.StartNew(() =>
            {
                var Tasks = new Task[] 
                { 
                    DeveloperEntityFileGenerator.WriteToDiskAsync(thisED),
                    DesignerEntityFileGenerator.WriteToDiskAsync(thisED)
                };
                Task.WaitAll(Tasks);
            });
        }

        #endregion

        #region DeveloperEntityFileGenerator

        /// <summary>
        /// File generator for the developer entity file.  Once created, it will not be regenerated.
        /// This file is for end developers to add custom behavior, business rules, etc.
        /// </summary>
        private class DeveloperEntityFileGenerator
        {
            #region WriteToDiskAsync

            /// <summary>
            /// Creates and writes to disk the developer entity file. If this file already exists, 
            /// it will NOT be overwritten.
            /// </summary>
            public static Task WriteToDiskAsync(EntityDef thisED)
            {
                return Task.Factory.StartNew(() =>
                {
                    var ThisFileGenerator = new DeveloperEntityFileGenerator();
                    ThisFileGenerator.WriteToDisk(thisED);
                });
            }

            #endregion

            #region WriteToDisk

            /// <summary>
            /// Creates and writes to disk the developer entity file. If this file already exists, 
            /// it will NOT be overwritten.
            /// </summary>
            private void WriteToDisk(EntityDef thisED)
            {
                var ThisFB = new FileBuilder();
                ThisFB.ProjectName = thisED.Domain.ProjectName;
                ThisFB.Directory = thisED.EntitiesDirectoryPath;
                ThisFB.FileName = thisED.FileNames.Class();
                ThisFB.Namespace = thisED.Namespace;

                // Add the using namespaces for this file.
                ThisFB.UsingNamespaces.Add("System");
                ThisFB.UsingNamespaces.Add(thisED.Domain.FrameworkNamespace);

                // Add the class.
                ThisFB.CodeUnits.Add(new DeveloperEntityClassBuilder(thisED));

                // Write this file to disk - THIS FILE SHOULD NEVER BE OVERWRITTEN.
                ThisFB.WriteToDisk(OverwriteExistingFile.No);
                // TODO: do not override file after initial development
                //ThisFB.WriteToDisk(OverwriteExistingFile.Yes);
            }

            #endregion

            #region DeveloperEntityClassBuilder

            /// <summary>
            /// Builds the develoer entity class.
            /// </summary>
            private class DeveloperEntityClassBuilder : ClassBuilder
            {
                #region Constructors

                public DeveloperEntityClassBuilder(EntityDef thisED)
                    : base(thisED.ClassNames.ClassName(), CodeGenerator.CSharp.Modifiers.PublicPartial)
                {
                    this.AddSetDefaultPropertyValuesProcedure(thisED);
                    this.AddToStringProcedure(thisED);
                }

                #endregion

                #region AddSetDefaultPropertyValuesProcedure

                /// <summary>
                /// Adds the DefaultPropertyValues procedure, which is used to set the default values 
                /// for the respective entity when a new entity of that type is created.
                /// </summary>
                public void AddSetDefaultPropertyValuesProcedure(EntityDef thisED)
                {
                    var ThisRegion = this.AddRegion("SetDefaultPropertyValues");
                    var ThisProcedure = ThisRegion.AddProcedure("SetDefaultPropertyValues", Modifier.InternalOverride);
                    ThisProcedure.Documentation.Summary = "Sets the properties of a new entity to its default values.";
                    if ((thisED.HasFriendlyIDPropertyDef))
                        ThisProcedure.Body.Add("this.SetFriendlyIdAsync();");
                    ThisProcedure.Body.Add("this.{0} = Guid.NewGuid();", thisED.PrimaryKeyPropertyDef.Name);

                    // Adds a link back to the parent entity, if there is one.
                    bool OnFirstParent = true;
                    foreach (var ThisPD in thisED.PropertyDefs.EntityProperties)
                    {
                        if (OnFirstParent)
                        {
                            ThisProcedure.Body.AddBlankLine();
                            ThisProcedure.Body.Add("if (this.ParentEntity is {0})", ThisPD.DataTypeEntityDef.ClassNames.ClassName());
                        }
                        else
                        {
                            ThisProcedure.Body.Add("else if (this.ParentEntity is {0})", ThisPD.DataTypeEntityDef.ClassNames.ClassName());
                        }
                        ThisProcedure.Body.Add("    this.{0} = this.ParentEntity as {1};", ThisPD.Name, ThisPD.DataTypeEntityDef.ClassNames.ClassName());
                        OnFirstParent = false;
                    }
                }

                #endregion

                #region AddToStringProcedure

                /// <summary>
                /// Add the ToString procedure, used to provide a reasonable string represenation of this entity.
                /// </summary>
                public void AddToStringProcedure(EntityDef thisED)
                {
                    var ThisRegion = this.AddRegion("ToString");
                    var ThisProcedure = ThisRegion.AddProcedure("ToString", Modifier.PublicOverride, "string");
                    ThisProcedure.Documentation.Summary = "Provides a user friendly string representation of this " + thisED.Name + ".";
                    ThisProcedure.Body.Add("// TODO: identify a user friendly string reresentation of a " + thisED.Name);
                    ThisProcedure.Body.Add("return base.ToString();");
                }

                #endregion

            }

            #endregion
        }

        #endregion

        #region DesignerEntityFileGenerator

        /// <summary>
        /// File generator for the auto-generated entity file.  This file will be re-created
        /// as the domain model is updated.
        /// </summary>
        private class DesignerEntityFileGenerator
        {
            #region WriteToDiskAsync

            /// <summary>
            /// Creates and writes to disk the Designer entity file. If this file already exists, 
            /// it will be overwritten.
            /// </summary>
            public static Task WriteToDiskAsync(EntityDef thisED)
            {
                return Task.Factory.StartNew(() =>
                {
                    var ThisFileGenerator = new DesignerEntityFileGenerator();
                    ThisFileGenerator.WriteToDisk(thisED);
                });
            }

            #endregion

            #region WriteToDisk

            /// <summary>
            /// Creates and writes to disk the Designer entity file. If this file already exists, 
            /// it will NOT be overwritten.
            /// </summary>
            private void WriteToDisk(EntityDef thisED)
            {
                var ThisFB = new FileBuilder();
                ThisFB.ProjectName = thisED.Domain.ProjectName;
                ThisFB.Directory = thisED.EntitiesDirectoryPath;
                ThisFB.FileName = thisED.FileNames.ClassDesigner();
                ThisFB.Namespace = thisED.Namespace;
                ThisFB.IsAutoGenerated = true;

                // Add the using namespaces for this file.
                ThisFB.UsingNamespaces.Add("System");
                ThisFB.UsingNamespaces.Add("System.Collections.Generic");
                ThisFB.UsingNamespaces.Add("System.Linq");
                ThisFB.UsingNamespaces.Add("System.Threading.Tasks");
                ThisFB.UsingNamespaces.Add(thisED.Domain.FrameworkNamespace);

                // Add the class.
                ThisFB.CodeUnits.Add(new DesignerEntityClassBuilder(thisED));

                // Write this file to disk.
                ThisFB.WriteToDisk(OverwriteExistingFile.Yes);
            }

            #endregion

            #region DesignerEntityClassBuilder

            /// <summary>
            /// Builds the auto-generated entity class.
            /// </summary>
            private class DesignerEntityClassBuilder : ClassBuilder
            {
                #region Constructors

                public DesignerEntityClassBuilder(EntityDef thisED)
                    : base(thisED.ClassNames.ClassName(), CodeGenerator.CSharp.Modifiers.PublicPartial)
                {
                    this.BaseClass = "Entity";
                    this.Documentation.Summary = thisED.Purpose;
                    this.AddConstrucorProcedures(thisED);
                    this.AddSynchronizeDtoProcedure(thisED);
                    this.AddProperties(thisED);
                    this.AddSetFriendlyIdAsyncProcedure(thisED);
                    this.AddBuildObjectGraphCompletedProcedure(thisED);

                    if (thisED.Name == "User") this.AddUserSecurityProcedures(thisED);
                }

                #endregion

                #region AddConstrucorProcedures

                /// <summary>
                /// Adds the constructors.
                /// </summary>
                public void AddConstrucorProcedures(EntityDef thisED)
                {
                    var ThisRegion = this.AddRegion("Construcors");

                    var ConstructorWithAggregate = ThisRegion.AddConstructor(thisED.ClassNames.ClassName(), Modifier.Internal);
                    ConstructorWithAggregate.Documentation.Summary = "Creates a new instance of this " + thisED.Name + " entity linked to the identified Aggregate.";
                    ConstructorWithAggregate.Parameters.Add("aggregate", "Aggregate");
                    ConstructorWithAggregate.Base = "aggregate";

                    var ConstructorWithAggregateAndParentEntity = ThisRegion.AddConstructor(thisED.ClassNames.ClassName(), Modifier.Internal);
                    ConstructorWithAggregateAndParentEntity.Documentation.Summary = "Creates a new instance of this " + thisED.Name + " entity linked to the identified Aggregate and the identified parent entity.";
                    ConstructorWithAggregateAndParentEntity.Parameters.Add("aggregate", "Aggregate");
                    ConstructorWithAggregateAndParentEntity.Parameters.Add("parentEntity", "Entity");
                    ConstructorWithAggregateAndParentEntity.Base = "aggregate, parentEntity";

                }

                #endregion

                #region AddSynchronizeDtoProcedure

                /// <summary>
                /// Add the procedures that synchronize a DTO with an Entity
                /// </summary>
                public void AddSynchronizeDtoProcedure(EntityDef thisED)
                {
                    var ThisRegion = this.AddRegion("SynchronizeDto");

                    //
                    var InstanceProcedure = ThisRegion.AddProcedure("SynchronizeDto", Modifier.Internal);
                    InstanceProcedure.Documentation.Summary = "Synchronizes the identified list of DTOs with any changes made to this entity (i.e., if this entity has been modified, the corresponding DTO will be located in the list and its properties will be updated; if this is a new entity, a new DTO will be created, its properties set and added to the list; if flagged as deleted, then the matching DTO in the list will have its state set to deleted.";
                    InstanceProcedure.Parameters.Add("theseDtos", "List<" + thisED.ClassNames.Dto() + ">");
                    InstanceProcedure.Body.Add("var ThisDTO = this.GetOrCreateDto(theseDtos);");
                    InstanceProcedure.Body.Add("bool AtLeastOnePropertyChanged;");
                    InstanceProcedure.Body.Add("SynchronizeDto(this, ThisDTO, out AtLeastOnePropertyChanged);");
                    InstanceProcedure.Body.AddBlankLine();
                    InstanceProcedure.Body.Add("if (this.FlaggedAsDeleted)");
                    InstanceProcedure.Body.Add("    ThisDTO.State = DtoChangedState.Deleted;");
                    InstanceProcedure.Body.AddBlankLine();
                    InstanceProcedure.Body.Add("else if (ThisDTO.State != DtoChangedState.New && AtLeastOnePropertyChanged)");
                    InstanceProcedure.Body.Add("    ThisDTO.State = DtoChangedState.Modified;");

                    // The private-static procedure that does the actual synchronization.
                    var StaticProcedure = ThisRegion.AddProcedure("SynchronizeDto", Modifier.PrivateStatic);
                    StaticProcedure.Documentation.Summary = "Synchronizes the properties of the identified DTO with the current settings of the identified entity.  If it is determined that one of the property values has changed, the atLeastOnePropertyChanged will be set to true.";
                    StaticProcedure.Parameters.Add("thisEntity", thisED.ClassNames.ClassName());
                    StaticProcedure.Parameters.Add("thisDto", thisED.ClassNames.Dto());
                    StaticProcedure.Parameters.Add("atLeastOnePropertyChanged", "bool", PassedBy.Out);
                    StaticProcedure.Body.Add("atLeastOnePropertyChanged = false;");
                    StaticProcedure.Body.AddBlankLine();

                    // Synchronize ScalarProperties
                    foreach (PropertyDef ThisPD in thisED.PropertyDefs.ScalarProperties)
                    {
                        StaticProcedure.Body.Add("if (thisEntity.{0} != null && thisDto.{1} != thisEntity.{1})", ThisPD.Names.BackingPropertyField(), ThisPD.Names.Field());
                        StaticProcedure.Body.Add("{");
                        StaticProcedure.Body.Add("    thisDto.{0} = thisEntity.{0};", ThisPD.Names.Field());
                        StaticProcedure.Body.Add("    atLeastOnePropertyChanged = true;");
                        StaticProcedure.Body.Add("}");
                        StaticProcedure.Body.AddBlankLine();
                    }

                    // Synchronize EntityProperties
                    foreach (PropertyDef ThisPD in thisED.PropertyDefs.EntityProperties)
                    {
                        StaticProcedure.Body.Add("if (thisEntity.{0} != null)", ThisPD.Names.BackingPropertyField());
                        StaticProcedure.Body.Add("{");
                        if (!ThisPD.IsNullable)
                        {
                            StaticProcedure.Body.Add("    if (thisEntity.{0} == null) throw new Exception(\"{0} is not allowed to be null;\");", ThisPD.Name);
                            StaticProcedure.Body.Add("    if (thisDto.{0} != thisEntity.{1}.ID)", ThisPD.Names.Field(), ThisPD.Name);
                            StaticProcedure.Body.Add("    {");
                            StaticProcedure.Body.Add("        thisDto.{0} = thisEntity.{1}.ID;", ThisPD.Names.Field(), ThisPD.Name);
                            StaticProcedure.Body.Add("        atLeastOnePropertyChanged = true;");
                            StaticProcedure.Body.Add("    }");
                        }
                        else
                        {
                            StaticProcedure.Body.Add("    if (thisDto.{0} != null && thisEntity.{1} == null)", ThisPD.Names.Field(), ThisPD.Name);
                            StaticProcedure.Body.Add("    {");
                            StaticProcedure.Body.Add("        thisDto.{0} = null;", ThisPD.Names.Field());
                            StaticProcedure.Body.Add("        atLeastOnePropertyChanged = true;");
                            StaticProcedure.Body.Add("    }");
                            StaticProcedure.Body.Add("    else if (thisEntity.{0} != null && (thisDto.{1} != thisEntity.{0}.ID))", ThisPD.Name, ThisPD.Names.Field());
                            StaticProcedure.Body.Add("    {");
                            StaticProcedure.Body.Add("        thisDto.{0} = thisEntity.{1}.ID;", ThisPD.Names.Field(), ThisPD.Name);
                            StaticProcedure.Body.Add("        atLeastOnePropertyChanged = true;");
                            StaticProcedure.Body.Add("    }");
                        }
                        StaticProcedure.Body.Add("}");
                        StaticProcedure.Body.AddBlankLine();
                    }

                    // GetOrCreateDto
                    var GetOrCreateDtoProcedure = ThisRegion.AddProcedure("GetOrCreateDto", Modifier.Private, thisED.ClassNames.Dto());
                    GetOrCreateDtoProcedure.Documentation.Summary = "Gets the DTO object that corresponds to this entity from the identified list.  If one does not exists, then a new one will be created, added to the list and returned.";
                    GetOrCreateDtoProcedure.Parameters.Add("existingDtos", "List<" + thisED.ClassNames.Dto() + ">");
                    GetOrCreateDtoProcedure.Body.Add("var ThisDTO = existingDtos.FirstOrDefault(ThisDto => ThisDto.ID == this.ID) as " + thisED.ClassNames.Dto() + ";");
                    GetOrCreateDtoProcedure.Body.Add("if (ThisDTO == null)");
                    GetOrCreateDtoProcedure.Body.Add("{");
                    GetOrCreateDtoProcedure.Body.Add("    ThisDTO = new " + thisED.ClassNames.Dto() + "();");
                    GetOrCreateDtoProcedure.Body.Add("    existingDtos.Add(ThisDTO);");
                    GetOrCreateDtoProcedure.Body.Add("    ThisDTO.State = DtoChangedState.New;");
                    GetOrCreateDtoProcedure.Body.Add("}");
                    GetOrCreateDtoProcedure.Body.Add("return ThisDTO;");

                }

                #endregion

                #region AddProperties

                /// <summary>
                /// Adds the properties for the identified EntityDef.
                /// </summary>
                public void AddProperties(EntityDef thisED)
                {
                    var PropertiesRegion = this.AddRegion("Properties");

                    this.AddIdProperty(thisED, PropertiesRegion);
                    this.AddScalarAndEntityProperties(thisED, PropertiesRegion);
                    this.AddCollectionProperties(thisED, PropertiesRegion);
                    this.AddInitializePropertiesProcedures(thisED, PropertiesRegion);
                }

                /// <summary>
                /// Adds the ID property.
                /// </summary>
                private void AddIdProperty(EntityDef thisED, Region propertiesRegion)
                {
                    var ThisRegion = propertiesRegion.AddRegion("ID");
                    var ThisProperty = ThisRegion.AddProperty("ID", Modifier.PublicOverride, "Guid");
                    ThisProperty.Documentation.Summary = "Uniquely identifies this " + thisED.Name + " entity.";
                    ThisProperty.GetBody.Add("return this." + thisED.Name + "ID;");
                }

                /// <summary>
                /// Adds the property (and initialization methods) for each scalar or entity 
                /// PropertyDef in the identified EntityDef.
                /// </summary>
                private void AddScalarAndEntityProperties(EntityDef thisED, Region propertiesRegion)
                {
                    foreach (PropertyDef ThisPD in thisED.PropertyDefs.ScalarOrEntityProperties)
                    {
                        var ThisRegion = propertiesRegion.AddRegion(ThisPD.Name);

                        var ThisProperty = ThisRegion.AddProperty(ThisPD.Name, Modifier.Public, ThisPD.Dt.ToX());
                        ThisProperty.Documentation.Summary = ThisPD.Purpose;
                        ThisProperty.GetBody.Add("return this.{0}.Value;", ThisPD.Names.BackingProperty());

                        ThisProperty.SetBody.Add("if (this.{0}.Value == value) return;", ThisPD.Names.BackingProperty());
                        ThisProperty.SetBody.Add("{0} PreviousValue = this.{1}.Value;", ThisPD.Dt.ToX(), ThisPD.Names.BackingProperty());
                        ThisProperty.SetBody.Add("this.{0}Changing(PreviousValue, value);", ThisPD.Name);
                        ThisProperty.SetBody.Add("this.{0}.Value = value;", ThisPD.Names.BackingProperty());
                        ThisProperty.SetBody.Add("this.{0}Changed(PreviousValue, value);", ThisPD.Name);

                        var _with3 = ThisRegion.AddProcedure(ThisPD.Name + "Changing", Modifier.Partial);
                        _with3.Parameters.Add("previousValue", ThisPD.Dt.ToX());
                        _with3.Parameters.Add("newValue", ThisPD.Dt.ToX());

                        var _with4 = ThisRegion.AddProcedure(ThisPD.Name + "Changed", Modifier.Partial);
                        _with4.Parameters.Add("previousValue", ThisPD.Dt.ToX());
                        _with4.Parameters.Add("newValue", ThisPD.Dt.ToX());




                        string DataType = string.Format("ValueProperty<{0}>", ThisPD.Dt.ToX());
                        var BackingProperty = ThisRegion.AddProperty(ThisPD.Names.BackingProperty(), Modifier.Public, DataType, ThisPD.Names.BackingPropertyField());
                        BackingProperty.Documentation.Summary = "Backing property for the " + ThisPD.Name + " property.";
                        BackingProperty.GetBody.Add("if (this.{0} == null) this.ThrowUninitializedPropertyException(\"{0}.{1}\");", ThisPD.Names.BackingPropertyField(), thisED.Name, ThisPD.Name);
                        BackingProperty.GetBody.Add("return this.{0};", ThisPD.Names.BackingPropertyField());

                        var InitializePropertyProcedure = ThisRegion.AddProcedure("Initialize" + ThisPD.Names.BackingProperty(), Modifier.Internal);
                        InitializePropertyProcedure.Documentation.Summary = "Initializes the " + ThisPD.Names.BackingProperty() + " property.";
                        InitializePropertyProcedure.Body.Add("this.{0} = new {1}(\"{2}\", \"{3}\", this);", ThisPD.Names.BackingPropertyField(), DataType, ThisPD.Name, ThisPD.FriendlyName);
                        if (!ThisPD.IsNullable)
                            InitializePropertyProcedure.Body.Add("this.{0}.ValidationRules.Add(new RequiredValueRule(this.{0}));", ThisPD.Names.BackingPropertyField());
                        InitializePropertyProcedure.Body.Add("this.Properties.Add(this.{0});", ThisPD.Names.BackingPropertyField());
                        InitializePropertyProcedure.Body.Add("this.{0}Initialized();", ThisPD.Names.BackingProperty());

                        var InitializePropertyProcedureWithInitialValue = ThisRegion.AddProcedure("Initialize" + ThisPD.Names.BackingProperty(), Modifier.Internal);
                        InitializePropertyProcedureWithInitialValue.Documentation.Summary = "Initializes the " + ThisPD.Names.BackingProperty() + " property with the identified initial value.";
                        InitializePropertyProcedureWithInitialValue.Parameters.Add("initialValue", ThisPD.Dt.ToX());
                        InitializePropertyProcedureWithInitialValue.Body.Add("this.{0} = new {1}(\"{2}\", \"{3}\", this, initialValue);", ThisPD.Names.BackingPropertyField(), DataType, ThisPD.Name, ThisPD.FriendlyName);
                        if ((!ThisPD.IsNullable))
                            InitializePropertyProcedureWithInitialValue.Body.Add("this.{0}.ValidationRules.Add(new RequiredValueRule(this.{0}));", ThisPD.Names.BackingPropertyField());
                        InitializePropertyProcedureWithInitialValue.Body.Add("this.Properties.Add(this.{0});", ThisPD.Names.BackingPropertyField());
                        InitializePropertyProcedureWithInitialValue.Body.Add("this.{0}Initialized();", ThisPD.Names.BackingProperty());

                        ThisRegion.AddProcedure(ThisPD.Names.BackingProperty() + "Initialized", Modifier.Partial);

                    }
                }


                /// <summary>
                /// Adds the property (and initialization methods) for each collection PropertyDef 
                /// in the identified EntityDef.
                /// </summary>
                private void AddCollectionProperties(EntityDef thisED, Region propertiesRegion)
                {
                    foreach (PropertyDef ThisPD in thisED.PropertyDefs.CollectionProperties)
                    {
                        var ThisRegion = propertiesRegion.AddRegion(ThisPD.Name);
                        var ThisProperty = ThisRegion.AddProperty(ThisPD.Name, Modifier.Public, ThisPD.Dt.ToName());
                        ThisProperty.Documentation.Summary = ThisPD.Purpose;
                        ThisProperty.GetBody.Add("return this.{0}.Value;", ThisPD.Names.BackingProperty());

                        string DataType = string.Format("EntityCollectionProperty<{0}, {1}>", ThisPD.Dt.ToName(), ThisPD.Dt.ToBaseName());
                        var BackingProperty = ThisRegion.AddProperty(ThisPD.Names.BackingProperty(), Modifier.Public, DataType, ThisPD.Names.BackingPropertyField());
                        BackingProperty.Documentation.Summary = "Backing property for the " + ThisPD.Name + " property.";
                        BackingProperty.GetBody.Add("if (this.{0} == null) this.ThrowUninitializedPropertyException(\"{1}\");", ThisPD.Names.BackingPropertyField(), ThisPD.Name);
                        BackingProperty.GetBody.Add("return this.{0};", ThisPD.Names.BackingPropertyField());

                        var InitializePropertyProcedure = ThisRegion.AddProcedure("Initialize" + ThisPD.Names.BackingProperty(), Modifier.Internal);
                        InitializePropertyProcedure.Body.Add("var value = new {0}(this.Aggregate, this);", ThisPD.DataType);
                        InitializePropertyProcedure.Body.Add("this.{0} = new {1}(\"{2}\", \"{3}\", this, value);", ThisPD.Names.BackingPropertyField(), DataType, ThisPD.Name, ThisPD.FriendlyName);
                        InitializePropertyProcedure.Body.Add("this.Properties.Add(this.{0});", ThisPD.Names.BackingPropertyField());
                    }
                }

                /// <summary>
                /// Adds the InitializeAllProperties & InitializeAllScalarProperties methods.
                /// </summary>
                private void AddInitializePropertiesProcedures(EntityDef thisED, Region propertiesRegion)
                {
                    var InitializeAllPropertiesProcedure = propertiesRegion.AddRegion("InitializeAllProperties").AddProcedure("InitializeAllProperties", Modifier.InternalOverride);
                    InitializeAllPropertiesProcedure.Documentation.Summary = "Initializes all properties of this entity.";
                    foreach (var ThisPD in thisED.PropertyDefs)
                    {
                        InitializeAllPropertiesProcedure.Body.Add("this.Initialize{0}();", ThisPD.Names.BackingProperty());
                    }

                    var InitializeAllScalarPropertiesProcedure = propertiesRegion.AddRegion("InitializeAllScalarProperties").AddProcedure("InitializeAllScalarProperties", Modifier.Internal);
                    InitializeAllScalarPropertiesProcedure.Documentation.Summary = "";
                    InitializeAllScalarPropertiesProcedure.Parameters.Add("dto", thisED.ClassNames.Dto());
                    foreach (var ThisPD in thisED.PropertyDefs.ScalarProperties)
                    {
                        InitializeAllScalarPropertiesProcedure.Body.Add("this.Initialize{0}(dto.{1});", ThisPD.Names.BackingProperty(), ThisPD.Name);
                    }

                }

                #endregion

                #region AddSetFriendlyIdAsyncProcedure

                /// <summary>
                /// Adds the SetFriendlyIdAsync method, which gets the next friendly ID async and updates
                /// the FriendlyID property when complete.
                /// </summary>
                /// <param name="thisED"></param>
                private void AddSetFriendlyIdAsyncProcedure(EntityDef thisED)
                {
                    if (!thisED.HasFriendlyIDPropertyDef) return;

                    var ThisRegion = this.AddRegion("SetFriendlyIdAsync");
                    var ThisProcedure = ThisRegion.AddProcedure("SetFriendlyIdAsync", Modifier.Internal, "Task");
                    ThisProcedure.Documentation.Summary = "Gets the next friendly ID for this entity asynchronously and sets the FriendlyID property when that call returns.";
                    //ThisProcedure.Body.Add("var ThisServiceProxy = DomainDataServiceClient.Create();");
                    //ThisProcedure.Body.Add("ThisServiceProxy.GetNextFriendlyIDCompleted += delegate(object sender, DomainDataServiceClient.GetNextFriendlyIDCompletedEventArgs e)");
                    //ThisProcedure.Body.Add("{");
                    //ThisProcedure.Body.Add("    if (e.Error == null)");
                    //ThisProcedure.Body.Add("    {");
                    //ThisProcedure.Body.Add("        this.FriendlyID = e.Result.ToString();");
                    //ThisProcedure.Body.Add("    }");
                    //ThisProcedure.Body.Add("    else");
                    //ThisProcedure.Body.Add("    {");
                    //ThisProcedure.Body.Add("        throw new Exception(\"There was an unexpected problem getting the next friendly id for a(n) '{0}'.\", e.Error);", thisED.FriendlyName);
                    //ThisProcedure.Body.Add("    }");
                    //ThisProcedure.Body.Add("};");

                    //ThisProcedure.Body.Add("ThisServiceProxy.GetNextFriendlyIDAsync(\"{0}\");", thisED.Name);

                    ThisProcedure.Body.Add("return Task.Factory.StartNew(() =>");
                    ThisProcedure.Body.Add("{");
                    ThisProcedure.Body.Add("    var ThisServiceProxy = DomainDataServiceClient.Create();");
                    ThisProcedure.Body.Add("    this.FriendlyID = ThisServiceProxy.GetNextFriendlyID(\"{0}\").ToString();", thisED.Name);
                    ThisProcedure.Body.Add("});");
                }

                #endregion

                #region AddBuildObjectGraphCompletedProcedure

                /// <summary>
                /// Adds the BuildObjectGraphCompleted handler method.
                /// </summary>
                private void AddBuildObjectGraphCompletedProcedure(EntityDef thisED)
                {
                    Region ThisRegion = this.AddRegion("BuildObjectGraphCompleted");
                    var BuildObjectGraphCompletedProcedure = ThisRegion.AddProcedure("BuildObjectGraphCompleted", Modifier.InternalOverride);
                    BuildObjectGraphCompletedProcedure.Documentation.Summary = "Handles when the object graph this entity belongs to has finished being built.";
                    BuildObjectGraphCompletedProcedure.Body.Add("this.PostBuildObjectGraphInitialization();");

                    var PostBuildObjectGraphInitializationProcedure = ThisRegion.AddProcedure("PostBuildObjectGraphInitialization", Modifier.Partial);
                    PostBuildObjectGraphInitializationProcedure.Documentation.Summary = "Partial method that enables the developer to handle when the object graph that this entity belongs to has finished being built.";

                }

                #endregion

                #region AddUserSecurityProcedures

                private void AddUserSecurityProcedures(EntityDef thisED)
                {
                    Region ThisRegion = this.AddRegion("Security");
                    this.AddUserRolesSecurity(ThisRegion, thisED);
                    this.AddUserPermissionsSecurity(ThisRegion, thisED);
                    this.AddUserCanPerformSecurity(ThisRegion, thisED.Domain);
                }

                /// <summary>
                /// AddUserRolesSecurity
                /// </summary>
                private void AddUserRolesSecurity(Region thisRegion, EntityDef thisED)
                {
                    var _with1 = thisRegion.AddRegion("Roles");
                    //--------------------------------------------------------------------------------------
                    // IsMemberOf
                    //--------------------------------------------------------------------------------------
                    var _with2 = _with1.AddProcedure("IsMemberOf", Modifier.Public, "bool");
                    _with2.Documentation.Summary = "To identify if this user is a member of the identified role.";
                    _with2.Parameters.Add("roleName", "string", "The name of the role.");
                    _with2.Body.Add("return this.FindRole(roleName) != null;");

                    //--------------------------------------------------------------------------------------
                    // IsMemberOfAdminRole
                    //--------------------------------------------------------------------------------------
                    var _with3 = _with1.AddProcedure("IsMemberOfAdminRole", Modifier.Public, "bool");
                    _with3.Documentation.Summary = "To identify if this user is a member of the identified role.";
                    _with3.Body.Add("return this.FindRole(\"Admin\") != null;");

                    //--------------------------------------------------------------------------------------
                    //GrantMembership
                    //--------------------------------------------------------------------------------------
                    var _with4 = _with1.AddProcedure("GrantMembership", Modifier.Public, "User_Role");
                    _with4.Documentation.Summary = "Grants this user membership into the identified role.";
                    _with4.Documentation.Remarks = "If the user is already a member of the identified role, then nothing will happen.";
                    _with4.Documentation.Returns = "The User_Role entity that associates this user with the identified role.";
                    _with4.Parameters.Add("thisRole", "Role", "The role to grant membership to.");
                    var _with5 = _with4.Body;
                    _with5.Add("User_Role ThisUser_Role = this.User_Roles.Find(thisRole);");
                    _with5.Add("if ((ThisUser_Role == null))");
                    _with5.Add("{");
                    _with5.Add("    ThisUser_Role = this.User_Roles.CreateNew();");
                    _with5.Add("    ThisUser_Role.User = this;");
                    _with5.Add("    ThisUser_Role.Role = thisRole;");
                    _with5.Add("    this.User_Roles.Add(ThisUser_Role);");
                    _with5.Add("}");
                    _with5.Add("return ThisUser_Role;");


                    //--------------------------------------------------------------------------------------
                    //RevokeMembership
                    //--------------------------------------------------------------------------------------
                    var _with6 = _with1.AddProcedure("RevokeMembership", Modifier.Public, "User_Role");
                    _with6.Documentation.Summary = "To remove this user from being a member of the identified role.";
                    _with6.Documentation.Remarks = "If the user is not a member of the identified role, then nothing will happen.";
                    _with6.Documentation.Returns = "The User_Role entity that was removed.";
                    _with6.Parameters.Add("thisRole", "Role", "The role to revoke membership from.");
                    var _with7 = _with6.Body;
                    _with7.Add("User_Role ThisUser_Role = this.User_Roles.Find(thisRole);");
                    _with7.Add("if (ThisUser_Role != null) ThisUser_Role.Delete();");
                    _with7.Add("return ThisUser_Role;");

                    //----------------------------------------------------------------------------------
                    //FindRole
                    //----------------------------------------------------------------------------------
                    var _with8 = _with1.AddProcedure("FindRole", Modifier.Private, "Role");
                    _with8.Documentation.Summary = "To search this user's roles for the role with the identified role name.";
                    _with8.Documentation.Returns = "The role with the identified name, if this user has been granted membership to the role; otherwise Nothing.";
                    _with8.Parameters.Add("roleName", "string", "The name of the role to search for.");
                    var _with9 = _with8.Body;
                    _with9.Add("foreach (User_Role ThisUser_Role in this.User_Roles)");
                    _with9.Add("if (ThisUser_Role.Role.Name == roleName)");
                    _with9.Add("    return ThisUser_Role.Role;");
                    _with9.Add("return null;");

                }

                /// <summary>
                /// AddUserPermissionsSecurity
                /// </summary>
                private void AddUserPermissionsSecurity(Region thisRegion, EntityDef thisED)
                {
                    var _with10 = thisRegion.AddRegion("Permissions");

                    //--------------------------------------------------------------------------------------
                    //GrantPermission
                    //--------------------------------------------------------------------------------------
                    var _with11 = _with10.AddProcedure("GrantPermission", Modifier.Public, "User_Permission");
                    _with11.Documentation.Summary = "To grant this user the identified permission.";
                    _with11.Documentation.Remarks = "If this user already has this permission, then nothing will happen.";
                    _with11.Documentation.Returns = "The User_Permission entity that represents the granted permission.";
                    _with11.Parameters.Add("thisPermission", "Permission", "The permission to grant.");
                    var _with12 = _with11.Body;
                    _with12.Add("var ThisUser_Permission = this.User_Permissions.Find(thisPermission);");
                    _with12.Add("if(ThisUser_Permission == null)");
                    _with12.Add("{");
                    _with12.Add("    ThisUser_Permission = this.User_Permissions.AddNew();");
                    _with12.Add("    ThisUser_Permission.User = this;");
                    _with12.Add("    ThisUser_Permission.Permission = thisPermission;");
                    _with12.Add("    this.User_Permissions.Add(ThisUser_Permission);");
                    _with12.Add("}");
                    _with12.Add("this.RevokeDeniedPermission(thisPermission);");
                    _with12.Add("return ThisUser_Permission;");

                    //----------------------------------------------------------------------------------
                    //DenyPermission
                    //----------------------------------------------------------------------------------
                    var _with13 = _with10.AddProcedure("DenyPermission", Modifier.Public, "User_DeniedPermission");
                    _with13.Documentation.Summary = "To explicitly deny this user the identified permission.";
                    _with13.Documentation.Remarks = "Any explicitly denied permission will take precedence over any inherited permission.  Any user in the Admin role cannot be explicitly denied a permission  Attempting to do so will result in an exception.  If this user had been granted the identifed permission, it will be removed.";
                    _with13.Documentation.Returns = "The User_DeniedPermission entity that associates this user with the denied permission.";
                    _with13.Parameters.Add("thisPermission", "Permission", "The permission to deny.");
                    var _with14 = _with13.Body;
                    _with14.Add("if (this.IsMemberOfAdminRole()) throw new Exception(\"Cannot deny permission to a user in the admin role.\");");
                    _with14.AddBlankLine();
                    _with14.Add("User_DeniedPermission ThisUser_DeniedPermission = this.User_DeniedPermissions.Find(thisPermission);");
                    _with14.Add("if ((ThisUser_DeniedPermission == null))");
                    _with14.Add("{");
                    _with14.Add("    ThisUser_DeniedPermission = this.User_DeniedPermissions.AddNew();");
                    _with14.Add("    ThisUser_DeniedPermission.User = this;");
                    _with14.Add("    ThisUser_DeniedPermission.DeniedPermission = thisPermission;");
                    _with14.Add("    this.User_DeniedPermissions.Add(ThisUser_DeniedPermission);");
                    _with14.Add("}");
                    _with14.Add("this.RevokePermission(thisPermission);");
                    _with14.Add("return ThisUser_DeniedPermission;");

                    //----------------------------------------------------------------------------------
                    //RevokeDeniedPermission
                    //----------------------------------------------------------------------------------
                    var _with15 = _with10.AddProcedure("RevokeDeniedPermission", Modifier.Public, "User_DeniedPermission");
                    _with15.Documentation.Summary = "To revoke the identified denied permission.";
                    _with15.Documentation.Remarks = "If this user has not been explicitly denied the identified permission, then nothing will happen.";
                    _with15.Documentation.Returns = "The User_DeniedPermission entity that associates this user with the denied permission.";
                    _with15.Parameters.Add("thisPermission", "Permission", "The permission that has beend denied.");
                    var _with16 = _with15.Body;
                    _with16.Add("User_DeniedPermission ThisUser_DeniedPermission = this.User_DeniedPermissions.Find(thisPermission);");
                    _with16.Add("if (ThisUser_DeniedPermission != null) ThisUser_DeniedPermission.Delete();");
                    _with16.Add("return ThisUser_DeniedPermission;");

                    //----------------------------------------------------------------------------------
                    //FindPermission
                    //----------------------------------------------------------------------------------
                    var _with17 = _with10.AddProcedure("FindPermission", Modifier.Private, "Permission");
                    _with17.Documentation.Summary = "To search this user's permissions for the permission with the identified permission name.";
                    _with17.Documentation.Remarks = "This will include both direct and inherited (from any role membership) permissions.";
                    _with17.Documentation.Returns = "The permission with the identified name, if this user has been granted such a permission; otherwise Nothing.";
                    _with17.Parameters.Add("permissionName", "string", "The name of the permission to search for.");
                    var _with18 = _with17.Body;
                    _with18.Add("Permission ThisPermission = this.FindDirectPermission(permissionName);");
                    _with18.Add("if (ThisPermission == null) ThisPermission = this.FindInheritedPermission(permissionName);");
                    _with18.Add("return ThisPermission;");

                    //----------------------------------------------------------------------------------
                    //FindDirectPermission
                    //----------------------------------------------------------------------------------
                    var _with19 = _with10.AddProcedure("FindDirectPermission", Modifier.Private, "Permission");
                    _with19.Documentation.Summary = "To search this user's direct permissions for the permission with the identified permission name.";
                    _with19.Documentation.Remarks = "This will not search any inherited (from any role membership) permissions.";
                    _with19.Documentation.Returns = "The permission with the identified name, if this user has been directly granted such a permission; otherwise Nothing.";
                    _with19.Parameters.Add("permissionName", "string", "The name of the permission to search for.");
                    var _with20 = _with19.Body;
                    _with20.Add("foreach (User_Permission ThisUser_Permission in this.User_Permissions)");
                    _with20.Add("    if (ThisUser_Permission.Permission.Name == permissionName)");
                    _with20.Add("        return ThisUser_Permission.Permission;");
                    _with20.Add("return null;");

                    //----------------------------------------------------------------------------------
                    //FindInheritedPermission
                    //----------------------------------------------------------------------------------
                    var _with21 = _with10.AddProcedure("FindInheritedPermission", Modifier.Private, "Permission");
                    _with21.Documentation.Summary = "To search this user's inherited permissions (those from role membership) for the permission with the identified permission name.";
                    _with21.Documentation.Remarks = "This will not search any directly assigned permissions.";
                    _with21.Documentation.Returns = "The permission with the identified name, if this user has inherited such a permission; otherwise Nothing.";
                    _with21.Parameters.Add("permissionName", "string", "The name of the permission to search for.");
                    var _with22 = _with21.Body;
                    _with22.Add("foreach (User_Role ThisUser_Role in this.User_Roles)");
                    _with22.Add("{");
                    _with22.Add("    foreach (Role_Permission ThisRole_Permission in ThisUser_Role.Role.Role_Permissions)");
                    _with22.Add("    {");
                    _with22.Add("        if (ThisRole_Permission.Permission.Name == permissionName)");
                    _with22.Add("            return ThisRole_Permission.Permission;");
                    _with22.Add("    }");
                    _with22.Add("}");
                    _with22.Add("return null;");

                    //----------------------------------------------------------------------------------
                    //FindDeniedPermission
                    //----------------------------------------------------------------------------------
                    var _with23 = _with10.AddProcedure("FindDeniedPermission", Modifier.Private, "Permission");
                    _with23.Documentation.Summary = "To search this user's denied permissions for the permission with the identified permission name.";
                    _with23.Documentation.Returns = "The permission with the identified name, if this user has been denied such a permission; otherwise Nothing.";
                    _with23.Parameters.Add("permissionName", "string", "The name of the permission to search for.");
                    var _with24 = _with23.Body;
                    _with24.Add("foreach (User_DeniedPermission ThisUser_DeniedPermission in this.User_DeniedPermissions)");
                    _with24.Add("    if (ThisUser_DeniedPermission.DeniedPermission.Name == permissionName)");
                    _with24.Add("        return ThisUser_DeniedPermission.DeniedPermission;");
                    _with24.Add("return null;");

                    //----------------------------------------------------------------------------------
                    //RevokePermission
                    //----------------------------------------------------------------------------------
                    var _with25 = _with10.AddProcedure("RevokePermission", Modifier.Public, "User_Permission");
                    _with25.Documentation.Summary = "To revoke the identified permission from this user.";
                    _with25.Documentation.Remarks = "If this user does not have this permission, then nothing will happen.";
                    _with25.Documentation.Returns = "The User_Permission entity that was removed.";
                    _with25.Parameters.Add("thisPermission", "Permission", "The permission to revoke.");
                    var _with26 = _with25.Body;
                    _with26.Add("User_Permission ThisUser_Permission = this.User_Permissions.Find(thisPermission);");
                    _with26.Add("if (ThisUser_Permission != null) ThisUser_Permission.Delete();");
                    _with26.Add("return ThisUser_Permission;");

                    //--------------------------------------------------------------------------------------
                    // HasEffectivePermission
                    //--------------------------------------------------------------------------------------
                    var _with27 = _with10.AddProcedure("HasEffectivePermission", Modifier.Public, "bool");
                    _with27.Documentation.Summary = "To identify if this user effectively (either directly or inherited from role membership) has the identified permission.";
                    _with27.Parameters.Add("permissionName", "string", "The name of the permission.");
                    _with27.Body.Add("return this.FindPermission(permissionName) != null;");

                    //--------------------------------------------------------------------------------------
                    // HasDirectPermission
                    //--------------------------------------------------------------------------------------
                    var _with28 = _with10.AddProcedure("HasDirectPermission", Modifier.Public, "bool");
                    _with28.Documentation.Summary = "To identify if this user has been directly assinged the identified permission.";
                    _with28.Parameters.Add("permissionName", "string", "The name of the permission.");
                    _with28.Body.Add("return this.FindDirectPermission(permissionName) != null;");

                    //--------------------------------------------------------------------------------------
                    // HasInheritedPermission
                    //--------------------------------------------------------------------------------------
                    var _with29 = _with10.AddProcedure("HasInheritedPermission", Modifier.Public, "bool");
                    _with29.Documentation.Summary = "To identify if this user has inherited the identified permission (from membership in a role).";
                    _with29.Parameters.Add("permissionName", "string", "The name of the permission.");
                    _with29.Body.Add("return this.FindInheritedPermission(permissionName) != null;");

                    //--------------------------------------------------------------------------------------
                    // HasDeniedPermission
                    //--------------------------------------------------------------------------------------
                    var _with30 = _with10.AddProcedure("HasDeniedPermission", Modifier.Public, "bool");
                    _with30.Documentation.Summary = "To identify if this user has been denied the identified permission.";
                    _with30.Parameters.Add("permissionName", "string", "The name of the permission.");
                    _with30.Body.Add("return this.FindDeniedPermission(permissionName) != null;");
                }

                /// <summary>
                /// AddUserCanPerformSecurity
                /// </summary>
                private void AddUserCanPerformSecurity(Region thisRegion, Domain thisDomain)
                {
                    var _with31 = thisRegion.AddRegion("Can Perform");
                    //--------------------------------------------------------------------------------------
                    // CanPerformOperation
                    //--------------------------------------------------------------------------------------
                    var _with32 = _with31.AddProcedure("CanPerformOperation", Modifier.Public, "bool");
                    _with32.Documentation.Summary = "Identifies if this user can perform the identified operation (permission).";
                    _with32.Parameters.Add("operation", "string", "The operation to check.");
                    _with32.Body.Add("return (this.IsMemberOfAdminRole() || this.HasEffectivePermission(operation)) && !this.HasDeniedPermission(operation);");

                    //----------------------------------------------------------------------------------
                    //Strongly-Typed Can Procedures
                    //----------------------------------------------------------------------------------
                    var _with33 = _with31.AddRegion("Strongly-Typed Can Create/Read/Update/Delete Entity");
                    foreach (EntityDef ThisED in thisDomain.EntityDefs)
                    {
                        dynamic EntityRegion = _with33.AddRegion(ThisED.Name);
                        this.AddStronglyTypedCanProcedure(EntityRegion, "Create", ThisED);
                        this.AddStronglyTypedCanProcedure(EntityRegion, "Read", ThisED);
                        this.AddStronglyTypedCanProcedure(EntityRegion, "Update", ThisED);
                        this.AddStronglyTypedCanProcedure(EntityRegion, "Delete", ThisED);
                    }

                    //----------------------------------------------------------------------------------
                    //Can Procedures
                    //----------------------------------------------------------------------------------
                    dynamic CanRegion = _with31.AddRegion("Can Create/Read/Update/Delete Entity");
                    this.AddCanProcedure(CanRegion, "Create", true);
                    this.AddCanProcedure(CanRegion, "Read", true);
                    this.AddCanProcedure(CanRegion, "Update", true);
                    this.AddCanProcedure(CanRegion, "Delete", true);


                    //----------------------------------------------------------------------------------
                    //VerifyCanPerformOperation
                    //----------------------------------------------------------------------------------
                    var _with34 = _with31.AddProcedure("VerifyCanPerformOperation", Modifier.Public);
                    _with34.Documentation.Summary = "Verifies that this user can perform the identified operation.";
                    _with34.Documentation.Remarks = "If this user is authorized to perform the operation, then nothing will happen; if this user is not authorized, the NotAuthorizedException exception will be thrown.";
                    _with34.Parameters.Add("operation", "string", "The operation to verify that this user can permform.");
                    var _with35 = _with34.Body;
                    _with35.Add("if (!this.CanPerformOperation(operation))");
                    _with35.Add("    throw new NotAuthorizedException(this.UserName, operation);");

                    //----------------------------------------------------------------------------------
                    //Verify Can Create, Read, Update & Delete
                    //----------------------------------------------------------------------------------
                    this.AddVerifyCan(thisRegion, "Create");
                    this.AddVerifyCan(thisRegion, "Read");
                    this.AddVerifyCan(thisRegion, "Update");
                    this.AddVerifyCan(thisRegion, "Delete");

                    //----------------------------------------------------------------------------------
                    //Verify Strongly-Typed Can Create, Read, Update & Delete
                    //----------------------------------------------------------------------------------
                    foreach (EntityDef thisED in thisDomain.EntityDefs)
                    {
                        Region EntityRegion = _with31.AddRegion(thisED.Name);
                        this.AddStronglyTypedVerifyCanProcedure(EntityRegion, "Create", thisED);
                        this.AddStronglyTypedVerifyCanProcedure(EntityRegion, "Read", thisED);
                        this.AddStronglyTypedVerifyCanProcedure(EntityRegion, "Update", thisED);
                        this.AddStronglyTypedVerifyCanProcedure(EntityRegion, "Delete", thisED);
                    }

                }

                /// <summary>
                /// AddVerifyCan
                /// </summary>
                private void AddVerifyCan(Region thisRegion, string operation)
                {
                    var _with36 = thisRegion.AddProcedure("VerifyCan" + operation, Modifier.Public);
                    _with36.Documentation.Summary = "Verifies that this user can " + operation.ToLower() + " the identified class of entities.";
                    _with36.Documentation.Remarks = "If this user is authorized to perform the operation, then nothing will happen; if this user is not authorized, the NotAuthorizedException exception will be thrown.";
                    //.Attributes.Add("ComponentModel.EditorBrowsable(ComponentModel.EditorBrowsableState.Advanced)")
                    _with36.Parameters.Add("entityName", "string", "The name of the entity to verify.");
                    //.Body.Add("Me.VerifyCanPerformOperation(""" & operation & """ & entityName)")
                    _with36.Body.Add("this.VerifyCanPerformOperation(\"" + operation + "\" + entityName);");

                }

                /// <summary>
                /// AddStronglyTypedVerifyCanProcedure
                /// </summary>
                private void AddStronglyTypedVerifyCanProcedure(Region thisRegion, string operation, EntityDef thisED)
                {
                    var _with37 = thisRegion.AddProcedure("VerifyCan" + operation + thisED.Name, Modifier.Public);
                    _with37.Documentation.Summary = "Verifies that this user has permission to " + operation.ToLower() + " a " + thisED.FriendlyName + " entity.";
                    _with37.Documentation.Remarks = "If this user is authorized to perform the operation, then nothing will happen; if this user is not authorized, the NotAuthorizedException exception will be thrown.";
                    //.Body.Add("Me.VerifyCanPerformOperation(""" & operation & thisED.Name & """)")
                    _with37.Body.Add("this.VerifyCanPerformOperation(\"" + operation + thisED.Name + "\");");

                }

                /// <summary>
                /// AddStronglyTypedCanProcedure
                /// </summary>
                private void AddStronglyTypedCanProcedure(Region thisRegion, string operation, EntityDef thisED)
                {
                    var _with38 = thisRegion.AddProcedure("Can" + operation + thisED.Name, Modifier.Public, "bool");
                    _with38.Documentation.Summary = "Identifies if this user has permission to " + operation.ToLower() + " a " + thisED.FriendlyName + " entity.";
                    _with38.Body.Add("return this.CanPerformOperation(\"" + operation + "" + thisED.Name + "\");");
                }

                /// <summary>
                /// AddCanProcedure
                /// </summary>
                private void AddCanProcedure(Region thisRegion, string operation, bool forUser)
                {
                    var _with39 = thisRegion.AddProcedure("Can" + operation, Modifier.Public, "bool");
                    _with39.Documentation.Summary = "Identifies if this " + (forUser ? "user" : "role") + " has permission to " + operation.ToLower() + " the entities of the identified type.";
                    _with39.Parameters.Add("entityName", "string", "The name of the entity to check.");
                    _with39.Body.Add("return this.CanPerformOperation(\"" + operation + "\" + entityName);");
                }


                #endregion
            }

            #endregion

        }

        #endregion
    }
}
